local L = AceLibrary:GetInstance("AceLocale-2.0"):new("Clique")
local dewdrop = AceLibrary("Dewdrop-2.0")

local listSelected = 0          -- index of clickCasts selected (0 for none)
local maybeDoubleClick = nil    -- listSelected prior to an unselect, in case of a double-click (to edit)
local editSet
local tempButton, tempModifiers, tempTexture

function Clique:SpellBookFrame_OnShow()
    self.hooks[SpellBookFrame].OnShow.orig(SpellBookFrame)

    -- This darkens the background of the options UI to match the spellbook more closely
    CliqueBackdropLeft:SetVertexColor(.7,.7,.7,1)
    CliqueBackdropRight:SetVertexColor(.7,.7,.7,1)
    CliqueFrame:SetBackdropBorderColor(.5,.5,.5,1)

    editSet = self.db.char[L"DEFAULT_FRIENDLY"]
    Clique:ListScrollUpdate()

    local button
    for i=2,8 do
        button = getglobal("SpellBookSkillLineTab"..i)
        if not button:IsVisible() then
            CliquePulloutTab:ClearAllPoints()
            CliquePulloutTab:SetPoint("TOPLEFT","SpellBookSkillLineTab"..(i-1),"BOTTOMLEFT",0,-17)			
            break
        end
    end
end

function Clique:SpellButton_OnClick()
    if not CliqueFrame:IsVisible() then
        self.hooks[this].OnClick.orig(this)
    elseif CliqueEditFrame:IsVisible() then
        -- We're editing a custom spell at the moment
        return
    else
        local id = SpellBook_GetSpellID(this:GetID());
        local texture = GetSpellTexture(id, SpellBookFrame.bookType)
        local name, rank = GetSpellName(id, SpellBookFrame.bookType)
        local a,c,s = IsAltKeyDown() or 0, IsControlKeyDown() or 0, IsShiftKeyDown() or 0
        this:SetChecked(nil)
        
        if rank and string.find(rank, "Passive") then 
            StaticPopup_Show("CLIQUE_PASSIVE_SKILL")
            return
        end
        
        local modifiers = 0
        modifiers = 0
        modifiers = bit.bor(modifiers, a * 1)
        modifiers = bit.bor(modifiers, c * 2)
        modifiers = bit.bor(modifiers, s * 4)
                
        local t = {}
        t.button = arg1
        t.texture = texture
        t.modifiers = modifiers
        t.name = name

   		if self:CheckBinding(arg1, t.modifiers) then
			return
		end

        local _,_,numrank = string.find(rank, L"RANK" .. " (%d+)")
        t.rank = numrank
                    
        table.insert(editSet, t)
        Clique:ListScrollUpdate()
        Clique:BuildActionTable()
    end
end

StaticPopupDialogs["CLIQUE_AUTO_SELF_CAST"] = {
	text = "请禁用暴雪的自动自我施法，否则Clique无法工作。",
	button1 = TEXT(OKAY),
	OnAccept = function()
	end,
	timeout = 0,
	hideOnEscape = 1
}


function Clique:CheckBinding(button, modifiers)
    for k,v in ipairs(editSet) do
        if modifiers == v.modifiers and button == v.button then
            self:Debug("Found an existing instance of %s and %d.", button, modifiers)
            StaticPopup_Show("CLIQUE_BINDING_PROBLEM")
			-- Stop the binding from happening
			return true
        end
    end
	-- Allow the new binding
    return nil
end

function Clique.ListScrollUpdate()
    local idx,button
    local offset = FauxScrollFrame_GetOffset(CliqueListScroll)
    local clickCasts = editSet

    FauxScrollFrame_Update(CliqueListScroll, table.getn(clickCasts), 6, 48 )

    Clique:SortList()
    CliqueListFrame:Show()
	
    for i=1,6 do
        idx = offset + i
        button = getglobal("CliqueList"..i)
        if idx<=table.getn(clickCasts) then
            Clique:FillListEntry("CliqueList"..i,idx)
            button:Show()
            if idx == listSelected then
                button.lockedHighlight = 1
                getglobal("CliqueList"..i.."Highlight"):Show()
            else
                button.lockedHighlight = nil
                getglobal("CliqueList"..i.."Highlight"):Hide()
            end
        else
            button:Hide()
        end
    end
    Clique:ValidateButtons()
end

function Clique.SortFunc(a,b)
    -- Calculate modifier score
    -- The more modifiers you have, the higher your score
    
    if a.name == b.name then
        if a.rank and b.rank then
            return a.rank < b.rank
        elseif a.action and b.action then 
            return a.action < b.action
        else 
            return a.modifiers < b.modifiers
        end
    else
        return a.name < b.name
    end
end

function Clique:SortList()
    table.sort(editSet, self.SortFunc)
end
                            
-- fills the members of the ClickListTemplate for button (string frame name) and ClickIdx index to clickCasts
function Clique:FillListEntry(button, clickIdx)
    local clickCasts = editSet[clickIdx]
    getglobal(button.."Icon"):SetTexture(clickCasts.texture or "Interface\\Icons\\INV_Gizmo_02")
    getglobal(button.."Name"):SetText(clickCasts.name or L"CUSTOM_SCRIPT")
    getglobal(button.."Rank"):SetText(clickCasts.rank and L"RANK" .. " " .. clickCasts.rank or "")
    getglobal(button.."Binding"):SetText(Clique:GetBindingText(clickIdx))
end

-- the tab attached to the spellbook that toggles the window
function Clique:Toggle()
    Clique:EditCancel()
    if CliqueFrame:IsVisible() then
        CliquePulloutTab:SetChecked(0)
        CliqueFrame:Hide()
        dewdrop:Close()        
    else
        CliquePulloutTab:SetChecked(1)
        CliqueFrame:Show()
        self:ValidateButtons()
    end
end

-- returns "Modifier+Modifier+Click" string for clickCasts index
function Clique:GetBindingText(clickIdx)
    local click = editSet[clickIdx]
    local alt = (bit.band(click.modifiers, 1) > 0) and "Alt+" or ""
    local control = (bit.band(click.modifiers, 2) > 0) and "Ctrl+" or ""
    local shift = (bit.band(click.modifiers, 4) > 0) and "Shift+" or ""
    return string.format("%s%s%s%s", alt,control,shift,click.button)
end

--[[ Grey button functions ]]

-- for both CliqueListFrame and CliqueEditFrame, turn buttons on and off
function Clique:ValidateButtons()
    if CliqueListFrame:IsVisible() then
        if listSelected==0 then
            CliqueButtonDelete:Disable()
            CliqueButtonEdit:Disable()
            CliqueButtonMax:Disable()
			Clique:SetTutorial("MAIN")
        else
			Clique:SetTutorial("SELECTED")
            CliqueButtonDelete:Enable()
            CliqueButtonEdit:Enable()
            if editSet[listSelected].rank then
                CliqueButtonMax:Enable()
            else
                CliqueButtonMax:Disable()
            end					
        end
    end
end

-- All the grey button clicks go through here
function Clique:ButtonOnClick(override)
    local source = override or this -- other parts of mod can call this, or the button itself did if not override

    if source==CliqueButtonOk then -- "Ok" : close list window
        CliqueFrame:Hide()
        CliquePulloutTab:SetChecked(0)
        dewdrop:Close()
    elseif source==CliqueButtonEdit then -- "Edit" : edit selected entry
        --Clique:FillListEntry("CliqueEditEntry",listSelected)
        tempButton = editSet[listSelected].button
        tempModifiers = editSet[listSelected].modifiers
        tempTexture = editSet[listSelected].texture
        
        CliqueEditBindingName:SetText(Clique:GetBindingText(listSelected))
        CliqueEditIconTexture:SetTexture(editSet[listSelected].texture or "Interface\\Icons\\INV_Gizmo_02")
        
        CliqueEditBox:SetText(editSet[listSelected].action)
        CliqueNameEditBox:SetText(editSet[listSelected].name)
		Clique:SetTutorial("EDIT")
        CliqueListFrame:Hide()
        CliqueEditFrame:Show()
        if editSet[listSelected].action then
            CliqueTextEditBox:Show()
            --CliqueNameEditBox:Show()
            --CliqueNameEditBox:EnableMouse(true)
            --CliqueNameEditBox:EnableKeyboard(true)
            CliqueNameEditBox.readOnly = false
            CliqueFocusGrabber:Show()
        else
            CliqueTextEditBox:Hide()
            --CliqueNameEditBox:Hide()
            --CliqueNameEditBox:EnableMouse(nil)
            --CliqueNameEditBox:EnableKeyboard(nil)
            CliqueNameEditBox.readOnly = true
            CliqueFocusGrabber:Hide()
        end
    elseif source==CliqueButtonDelete then -- "Delete" : remove entry from Clique.clickCasts
        table.remove(editSet,listSelected)
        listSelected = math.min(listSelected,table.getn(editSet))
        Clique:ListScrollUpdate()
        Clique:BuildActionTable()
    elseif source==CliqueButtonNew then -- "New" : add a new entry and go edit it
        table.insert(editSet,{name="Custom",button=L"BINDING_NOT_DEFINED",modifiers=0,action="",custom=true})
        listSelected = table.getn(editSet)
        Clique:ButtonOnClick(CliqueButtonEdit)
    elseif source==CliqueButtonSave then -- "Save" : Save editbox to Clique.clickCasts[x].action and go back to list
        if CliqueEditBox:IsVisible() then
            editSet[listSelected].action = CliqueEditBox:GetText()
        end
        editSet[listSelected].name = CliqueNameEditBox:GetText()
            -- Close the icon select frame either way

        Clique:EditCancel() -- go back to CliqueListFrame
        Clique:BuildActionTable()
        listSelected = 0
        dewdrop:Close()
		Clique:SetTutorial("SELECTED")
    elseif source==CliqueButtonCancel then -- "Cancel" : Abort changes and go back to list
        if CliqueIconSelectFrame:IsVisible() then
            CliqueIconSelectFrame:Hide()
            return
        end
        
        if CliqueEditFrame:IsVisible() then
            local entry = editSet[listSelected]
            if entry then
                if entry.custom and not entry.texture and entry.name == "Custom" and entry.button == L"BINDING_NOT_DEFINED" and entry.action == "" then
                    table.remove(editSet, listSelected)
                    listSelected = 0
                end
                if tempButton and entry.button ~= tempButton then
                    entry.button = tempButton
                end
                if tempModifiers and entry.modifiers ~= tempModifiers then
                    entry.modifiers = tempModifiers
                end
                if tempTexture and entry.texture ~= tempTexture then
                    entry.texture = tempTexture
                end
            end
        end
                
        Clique:EditCancel()
        dewdrop:Close()
		Clique:SetTutorial("SELECTED")
    elseif source==CliqueButtonMax then -- "Max Rank" : Remove rank value from table
        local click = editSet[listSelected]
        click.rank = nil
        --Clique:FillListEntry("CliqueEditEntry",listSelected)
        self:ListScrollUpdate()
        Clique:BuildActionTable()
	elseif source==CliqueButtonHelp then
		if CliqueTutorial:IsVisible() then
			CliqueTutorial:Hide()
		else 
			CliqueTutorial:Show()
		end
    end
    Clique:ValidateButtons()
end

--[[ CliqueListFrame functions (note most of work done in Clique.ButtonOnClick) ]]
-- the central list update function: shows help if needed, highlights, validates buttons, etc.  Call anytime clickCasts changes
-- when a list entry on ClickListFrame is clicked: select or unselect entry
function Clique:ListOnClick()
    local idx = FauxScrollFrame_GetOffset(CliqueListScroll) + this:GetID()
    maybeDoubleClick = idx
    listSelected = (listSelected==idx) and 0 or idx
    Clique:ListScrollUpdate()
end

-- when a list entry on ClickListFrame is double clicked: edit entry irregardless of selection
function Clique:ListOnDoubleClick()
    if maybeDoubleClick then
        listSelected = maybeDoubleClick
        Clique:ButtonOnClick(CliqueButtonEdit)
    end
end

--[[ CliqueEditFrame functions (note most of work done in Clique.ButtonOnClick) ]]
-- go from ClickEditFrame to ClickListFrame
function Clique:EditCancel()
    CliqueEditFrame:Hide()
    CliqueListFrame:Show()
    listSelected = 0
    Clique:ListScrollUpdate()
    CliqueIconSelectFrame:Hide()
end

-- updates key binding from when the user does a click combo on the entry above the edit box
function Clique:EditSelectedBinding()
    local click = editSet[listSelected]
    a = IsAltKeyDown() or 0
    s = IsShiftKeyDown() or 0 
    c = IsControlKeyDown() or 0 

    local modifiers = 0
    modifiers = bit.bor(modifiers, a * 1)
    modifiers = bit.bor(modifiers, c * 2)
    modifiers = bit.bor(modifiers, s * 4)

    if self:CheckBinding(arg1, modifiers) then
        return
    end
    
    click.button = arg1
    click.modifiers = modifiers

    CliqueEditBindingName:SetText(Clique:GetBindingText(listSelected))
    Clique:BuildActionTable()
end

function Clique:DropDown_OnShow()
    self.work = self:ClearTable(self.work)
    for k,v in pairs(self.db.char) do
        table.insert(self.work, k)
    end
    table.sort(self.work)

    UIDropDownMenu_Initialize(this, Clique.DropDown_Initialize);
    UIDropDownMenu_SetSelectedValue(CliqueDropDown, editSet)
	Clique:ListScrollUpdate()
end

function Clique.DropDown_Initialize()
    local info = {}

    for k,v in ipairs(Clique.work) do
        info = {};
        info.text = v;
        info.value = Clique.db.char[v];
        info.func = Clique.DropDown_OnClick;
        UIDropDownMenu_AddButton(info);
   end
end

function Clique.DropDown_OnClick()
    UIDropDownMenu_SetSelectedValue(CliqueDropDown, this.value);
    editSet = this.value
    listSelected = 0
    Clique:ListScrollUpdate()
end

function Clique:EnableTooltips()
    -- Set Dropdown selection
    self:SetTooltip(CliqueDropDown, L"TT_DROPDOWN")
--[[
    self:SetTooltip(CliqueList1, L"TT_LIST_ENTRY")
    self:SetTooltip(CliqueList2, L"TT_LIST_ENTRY")
    self:SetTooltip(CliqueList3, L"TT_LIST_ENTRY")
    self:SetTooltip(CliqueList4, L"TT_LIST_ENTRY")
    self:SetTooltip(CliqueList5, L"TT_LIST_ENTRY")
    self:SetTooltip(CliqueList6, L"TT_LIST_ENTRY")
--]]
    self:SetTooltip(CliqueButtonDelete, L"TT_DEL_BUTTON")
    self:SetTooltip(CliqueButtonMax, L"TT_MAX_BUTTON")
    self:SetTooltip(CliqueButtonNew, L"TT_NEW_BUTTON")
    self:SetTooltip(CliqueButtonEdit, L"TT_EDIT_BUTTON")
    self:SetTooltip(CliqueButtonOk, L"TT_OK_BUTTON")
    --self:SetTooltip(CliqueEditEntry, L"TT_EDIT_BINDING")

    --self:SetTooltip(CliqueNameEditBox, L"TT_NAME_EDITBOX")
    self:SetTooltip(CliqueButtonSave, L"TT_SAVE_BUTTON")
    self:SetTooltip(CliqueButtonCancel, L"TT_CANCEL_BUTTON")
    self:SetTooltip(CliqueTextEditBox, L"TT_TEXT_EDITBOX")
    self:SetTooltip(CliquePulloutTab, L"TT_PULLOUT_TAB")
end
    
function Clique:SetTutorial(screen)
	local message = ""
	
	if screen == "MAIN" then
		message = "在左侧的法术书选择要设置的技能，您可以同时按住ctrl/shift/alt等组合键设置该技能的快捷按键 。\n\n您还可以通过\"新建\"按钮添加自定义lua脚本。"
	elseif screen == "SELECTED" then
		message = "您选择了一个技能或自定义脚本。请自行选择到技能需要施放的对应等级（也可以点击\"最大\"来始终施放最高等级的法术）。\n\n您还可以通过\"编辑\"按钮更改技能的绑定，或更改自定义lua脚本。"
	elseif screen == "EDIT" then
		message = "您可以通过点击最上方按钮来重新设置绑定的按键。\n\n在自定义编辑框中，您可以点击鼠标右键弹出自定义函数列表。"
	end

	CliqueTutorialText:SetText(message)
end

function Clique:UpdateIconFrame()
    local MAX_MACROS = 18;
    local NUM_MACRO_ICONS_SHOWN = 20;
    local NUM_ICONS_PER_ROW = 5;
    local NUM_ICON_ROWS = 4;
    local MACRO_ICON_ROW_HEIGHT = 36;
    local macroPopupOffset = FauxScrollFrame_GetOffset(CliqueIconScrollFrame);
    local numMacroIcons = GetNumMacroIcons();

    -- Icon list
    for i=1, NUM_MACRO_ICONS_SHOWN do
        macroPopupIcon = getglobal("CliqueIcon"..i.."Icon");
        macroPopupButton = getglobal("CliqueIcon"..i);
        
        if not macroPopupButton.icon then
            macroPopupButton.icon = macroPopupIcon
        end
        
        index = (macroPopupOffset * NUM_ICONS_PER_ROW) + i;
        if ( index <= numMacroIcons ) then
            macroPopupIcon:SetTexture(GetMacroIconInfo(index));
            macroPopupButton:Show();
        else
            macroPopupIcon:SetTexture("");
            macroPopupButton:Hide();
        end
        macroPopupButton:SetChecked(nil);
    end
    
    FauxScrollFrame_Update(CliqueIconScrollFrame, ceil(numMacroIcons / NUM_ICONS_PER_ROW) , NUM_ICON_ROWS, MACRO_ICON_ROW_HEIGHT );
end

function Clique:SetSpellIcon(texture)
    editSet[listSelected].texture = texture
    CliqueEditIconTexture:SetTexture(texture)
end    

function Clique:ClickSpellIcon()
    Clique:SetSpellIcon(this.icon:GetTexture())
    CliqueIconSelectFrame:Hide()
    if editSet[listSelected].custom then
        CliqueTextEditBox:Show()
    end
end

--[[---------------------------------------------------------------------------------
  Handle the function dropdown, with registrations
----------------------------------------------------------------------------------]]

local function InsertEditBox(text)
    CliqueEditBox:Insert(text.."\n")
end

function Clique:RegisterCustomFunction(code, display, tooltip)
    if not code or type(code) ~= "string" then 
        error("Bad argument #1 to 'RegisterCustomFunction', (string expected got " .. type(code) .. ")")
    end
    if not display or type(display) ~= "string" then 
        error("Bad argument #2 to 'RegisterCustomFunction', (string expected got " .. type(display) .. ")")
    end
    if not tooltip or type(tooltip) ~= "string" then 
        error("Bad argument #3 to 'RegisterCustomFunction', (string expected got " .. type(tooltip) .. ")")
    end

    -- Create the table if it doesn't exist
    if not self.CustomFunctions then self.CustomFunctions = {} end
    
    local t = {["code"] = code, ["display"] = display, ["tooltip"] = tooltip}
    table.insert(self.CustomFunctions, t)
end

local function DewDropMenu()
    for k,v in ipairs(Clique.CustomFunctions) do  
        Clique:LevelDebug(2, "Adding Custom Function %s", v.display)
       dewdrop:AddLine(
           'text', v.display,
           'closeWhenClicked', true,
           'arg1', v.code,
           'func', InsertEditBox,
           'tooltipText', v.tooltip)
    end
end

function Clique:DropMenu(frame)    
    dewdrop:Open(frame, 'children', DewDropMenu, 'cursorX', true, 'cursorY', true)
end

StaticPopupDialogs["CLIQUE_PASSIVE_SKILL"] = {
	text = "你不能绑定被动技能。",
	button1 = TEXT(OKAY),
	OnAccept = function()
	end,
	timeout = 0,
	hideOnEscape = 1
}

StaticPopupDialogs["CLIQUE_BINDING_PROBLEM"] = {
	text = "这个组合已经绑定了。在重新绑定之前请删除旧的方案。",
	button1 = TEXT(OKAY),
	OnAccept = function()
	end,
	timeout = 0,
	hideOnEscape = 1
};

StaticPopupDialogs["CLIQUE_AUTOSELFCAST"] = {
	text = "如果启用暴雪自带的自动自我施法，Clique将不能正常工作。请在界面选项下禁用。",
	button1 = TEXT(OKAY),
	OnAccept = function()
	end,
	timeout = 0,
	hideOnEscape = 1
};
